﻿/*
 * usage:
 * 
 * create new instance in Game-Class AFTER Game.Initialize called and AFTER InputManager is created
 * (which already should be done usually).
 * using something like:
 * this.camera = new Camera(this, this.input, new Vector3(100, 100, 0), new Vector3(-1, 0, 0), Vector3.Up);
 * 
 * 
 * Camera movement will work if valid inputManager is given in constructor.
 * 
 * */

namespace CGShaderIntegration
{
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Input;

    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class CGCamera : GameComponent, IUpdateable
    {
        /// <summary>
        /// view Matrix
        /// </summary>
        public Matrix view;
        /// <summary>
        /// projection matrix
        /// </summary>
        public Matrix projection;

        /// <summary>
        /// target vector
        /// </summary>
        public Vector3 target;
        /// <summary>
        /// up vector
        /// </summary>
        public Vector3 up;

        /// <summary>
        /// mouse last position
        /// </summary>
        protected Vector2 mouseLastPos;   //changed by martink

        // notice: following vectors may be NOT up to date at runtime!
        // current camera stats are in the view matrix. variables below
        // are used for initialization and updates of debug-stats only!
        /// <summary>
        /// position of camera
        /// </summary>
        public Vector3 pos = Vector3.Zero;
        /// <summary>
        /// scale of camera
        /// </summary>
        public Vector3 scale = Vector3.Zero;
        /// <summary>
        /// rotation of camera
        /// </summary>
        public Quaternion rotation = new Quaternion();
        /// <summary>
        /// 
        /// </summary>
        internal CGInputManager input;

        /// <summary>
        /// Initializes a new instance of the <see cref="Camera"/> class.
        /// </summary>
        /// <param name="game">The game.</param>
        /// <param name="input">The input.</param>
        /// <param name="pos">The pos.</param>
        /// <param name="target">The target.</param>
        /// <param name="up">Up.</param>
        public CGCamera(Game game, CGInputManager input, Vector3 pos, Vector3 target, Vector3 up)
            : base(game)
        {
            this.pos = pos;
            this.target = target;
            this.up = up;
            this.input = input; //changed by kutza

            this.mouseLastPos = new Vector2(Mouse.GetState().X, Mouse.GetState().Y); //changed by martink

            this.view = Matrix.CreateLookAt(pos, target, up);
            this.projection = Matrix.CreatePerspectiveFieldOfView(
                MathHelper.PiOver4,
                (float)Game.GraphicsDevice.Viewport.Width / (float)Game.GraphicsDevice.Viewport.Height,
                1.0f,
                1000);
        }

        #region Methods
        /// <summary>
        /// Reference page contains code sample.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();
        }

        /// <summary>
        /// rotation factor
        /// </summary>
        internal const float RotationFactor = 0.02f;
        /// <summary>
        /// zoom factor
        /// </summary>
        internal const float ZoomFactor = 1.1f;
        /// <summary>
        /// zoom vector 
        /// </summary>
        internal Vector3 zoomVector = new Vector3(0, 0, ZoomFactor);
        /// <summary>
        /// fine scale factor
        /// </summary>
        internal const float FineScaleFactor = 0.1f;

        /// <summary>
        /// Called when the GameComponent needs to be updated. Override this method with component-specific update code.
        /// </summary>
        /// <param name="gameTime">Time elapsed since the last call to Update</param>
        public override void Update(GameTime gameTime)
        {
            //mouse 
            Vector2 deltaMousePos = new Vector2(Mouse.GetState().X - this.mouseLastPos.X, Mouse.GetState().Y - this.mouseLastPos.Y);
            if (deltaMousePos.Length() > 0.0f && Keyboard.GetState().IsKeyDown(Keys.LeftShift))
            {
                this.view *= Matrix.CreateFromYawPitchRoll((deltaMousePos.X / 70.0f), (deltaMousePos.Y / 70.0f), 0.0f);
            }
            this.mouseLastPos = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);

            //keyboard
            if (this.input == null) 
            {
                return;
            }

            // rotate -Y
            if (input.IsKeyPress(Keys.A)) 
            {
                this.view *= Matrix.CreateRotationY(-RotationFactor);
            }
            else if (input.IsKeyPress(Keys.D)) 
            {
                this.view *= Matrix.CreateRotationY(RotationFactor); // rotate +Y
            }

            if (input.IsKeyPress(Keys.Q)) 
            {
                this.view *= Matrix.CreateRotationX(-RotationFactor); // rotate -x
            }
            else if (input.IsKeyPress(Keys.E)) 
            {
                this.view *= Matrix.CreateRotationX(RotationFactor); // rotate +x
            }

            // zoom in
            if (input.IsKeyPress(Keys.W))
            {
                if (input.IsKeyPress(Keys.LeftControl))
                {
                    this.view *= Matrix.CreateTranslation(zoomVector * FineScaleFactor);
                }
                else
                {
                    this.view *= Matrix.CreateTranslation(zoomVector);
                }
            }
            else if (input.IsKeyPress(Keys.S))
            {
                if (input.IsKeyPress(Keys.LeftControl))
                {
                    this.view *= Matrix.CreateTranslation(-zoomVector * FineScaleFactor);  // zoom out
                }
                else
                {
                    this.view *= Matrix.CreateTranslation(-zoomVector);  // zoom out
                }
            }

            // move up / down
            if (input.IsKeyPress(Keys.R))
            {
                this.view *= Matrix.CreateTranslation(0, ZoomFactor, 0);
            }
            else if (input.IsKeyPress(Keys.F))
            {
                this.view *= Matrix.CreateTranslation(0, -ZoomFactor, 0);
            }
        }

        /// <summary>
        /// decompose view matrix and save results to members
        /// </summary>
        public void UpdateStats()
        {
            this.view.Decompose(out this.scale, out this.rotation, out this.pos);
        }
        #endregion
    }
}
